#include <system_error>
#include <stdexcept>
#include <cerrno>
#include <cstring>
#include <unistd.h>
#include "Channel.h"
#include "EventLoop.h"
#include "EpollDispatcher.h"

int32_t EpollDispatcher::epollCtl(int8_t optional)
{
    struct epoll_event ev;
    ev.data.fd = m_channel->getSocket();
    int events = 0;
    if (m_channel->getEvent() & static_cast<int>(FDEvent::ReadEvent))
    {
        events |= EPOLLIN;
    }
    if (m_channel->getEvent() & static_cast<int>(FDEvent::WriteEvent))
    {
        events |= EPOLLOUT;
    }
    ev.events = events;
    if (epoll_ctl(m_epfd, optional, m_channel->getSocket(), &ev) == -1)
    {
        perror("epoll_ctl failed");
        return -1;
    }

    return 0;
}

EpollDispatcher::EpollDispatcher(EventLoop *evLoop) : Dispatcher(evLoop)
{
    m_epfd = epoll_create(10);
    if (m_epfd == -1)
    {
        throw std::system_error(errno, std::generic_category(), "epoll_create failed");
    }

    m_events = new struct epoll_event[m_maxNode];
    m_name = "Epoll";
}

EpollDispatcher::~EpollDispatcher()
{
    close(m_epfd);
    delete[] m_events;
}

int32_t EpollDispatcher::add()
{
    if (epollCtl(EPOLL_CTL_ADD) == -1)
    {
        perror("epollCtl add failed");
        return -1;
    }
    return 0;
}

int32_t EpollDispatcher::remove()
{
    if (epollCtl(EPOLL_CTL_DEL) == -1)
    {
        perror("epollCtl delete failed");
        return -1;
    }
    // 通过channel释放对于的tcpConnection资源
    printf("释放channel -- channel->fd = %d\n", m_channel->getSocket());
    m_channel->destroyCallback(const_cast<void *>(m_channel->getArg()));
    return 0;
}

int32_t EpollDispatcher::modify()
{
    if (epollCtl(EPOLL_CTL_MOD) == -1)
    {
        perror("epollCtl modify failed");
        return -1;
    }
    return 0;
}

int32_t EpollDispatcher::dispatch(int timeout)
{
    int32_t count = epoll_wait(m_epfd, m_events, m_maxNode, timeout * 1000);
    if (count < 0)
    {
        perror("epoll_wait error");
    }
    for (int32_t i = 0; i < count; i++)
    {
        int32_t events = m_events[i].events;
        int fd = m_events[i].data.fd;
        if (events & (EPOLLHUP | EPOLLRDHUP | EPOLLERR))
        {
            // 对方断开了连接，删除fd
            //  epollRemove(Channel,evLoop);
            // printf("%s epollDispatch : epoll_wait cfd = %d对方断开了连接\n",evLoop->threadName, fd);
            continue;
        }
        if (events & EPOLLIN)
        {
            m_evLoop->eventActive(fd, (int)FDEvent::ReadEvent);
        }
        if (events & EPOLLOUT)
        {
            m_evLoop->eventActive(fd, (int)FDEvent::WriteEvent);
        }
    }
    return 0;
}
